package de.herberlin.server.ui;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import javax.swing.*;
import javax.swing.text.JTextComponent;

import de.herberlin.server.common.ConfigConstants;
import de.herberlin.server.common.Configuration;

/**
 * Panel displays options tabs for Bremsserver Configuration.
 * @author hans joachim herbertz
 * created 21.02.2004
 */
public class ConfigDialog extends JPanel {

	/**
	 * JTable editors for preferences
	 */
	private PreferenceEditor mimeTypeTab, extensionTab, aliasTab;
	/**
	 * Parent window
	 */
	private Window parent = null;
	/**
	 * First Tab server settings editor
	 */
	private ServerBasicsEditor httpServerEditor, proxyServerEditor,mailServerEditor;
	/**
	 * Constructor
	 */
	public ConfigDialog(Window parent, String mode)
		throws BackingStoreException {
		super(new BorderLayout());
		this.parent = parent;
		JTabbedPane tab = new JTabbedPane();
		if (ConfigConstants.MODE_HTTP.equals(mode)) {
			mimeTypeTab =
				new PreferenceEditor(
					Configuration.getMimeTypes(),
					PreferenceEditor.EDIT_TEXT);
			mimeTypeTab.setTableHeadlines(
				new String[] { "File Type", "Mime Type" });
			extensionTab =
				new PreferenceEditor(
					Configuration.getExtensions(),
					PreferenceEditor.EDIT_FILE);
			extensionTab.setTableHeadlines(
				new String[] { "File Type", "Application" });
			aliasTab =
				new PreferenceEditor(
					Configuration.getAliasDirectories(),
					PreferenceEditor.EDIT_DIRECTORIES);
			aliasTab.setTableHeadlines(
				new String[] { "Alias Name", "Directory" });
			httpServerEditor = new ServerBasicsEditor(Configuration.getPrefs());
			tab.add("Http Server", httpServerEditor);
			tab.add("MimeTypes", mimeTypeTab);
			tab.add("Extensions", extensionTab);
			tab.add("Alias", aliasTab);

		} else if (ConfigConstants.MODE_MAIL.equals(mode)){

			mailServerEditor = new MailBasicsEditor(Configuration.getPrefs());
			tab.add("Mail Server", mailServerEditor);

		} else if (ConfigConstants.MODE_PROXY.equals(mode)) {

			proxyServerEditor = new ProxyBasicsEditor(Configuration.getPrefs());
			tab.add("Proxy Server", proxyServerEditor);

		}

		add(tab, BorderLayout.CENTER);
		add(getBottomPanel(), BorderLayout.SOUTH);

	}

	/**
	 * Creates the panel at the bottom with ok - cancel buttons.
	 * @return
	 */
	private JPanel getBottomPanel() {
		JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		JButton b = new JButton("OK");
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (aliasTab!=null) aliasTab.store();
					if (extensionTab!=null) extensionTab.store();
					if (mimeTypeTab!=null)mimeTypeTab.store();
					if (httpServerEditor!=null) httpServerEditor.store();
					if (proxyServerEditor!=null) proxyServerEditor.store();
					if (mailServerEditor!=null) mailServerEditor.store();
					close();
				} catch (BackingStoreException e1) {
					e1.printStackTrace();
				}
			}
		});
		p.add(b);
		b = new JButton("Cancel");
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				close();
			}
		});
		p.add(b);
		b = new JButton("Help");
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (aliasTab!=null && aliasTab.isVisible()) {
					Help.show("/configuration/tab04");
				} else if (mimeTypeTab!=null && mimeTypeTab.isVisible()) {
					Help.show("/configuration/tab02");
				} else if (extensionTab!=null && extensionTab.isVisible()) {
					Help.show("/configuration/tab03");
				} else if (httpServerEditor!=null && httpServerEditor.isVisible()) {
					Help.show("/configuration/tab01");
				} else if (proxyServerEditor!=null && proxyServerEditor.isVisible()) {
					Help.show("/configuration/tab05");
				} else if (mailServerEditor!=null && mailServerEditor.isVisible()) {
					Help.show("/configuration/tab06");
				}
			}
		});
		p.add(b);
		return p;
	}

	// ---  The listener implementation for surrounding Frame ---
	/**
	 * surrounding frame may add a window listener to recieve window close events.
	 */
	public void addWindowListener(WindowListener w) {
		windowListenerList.add(w);
	}
	/**
	 * Remove window listener.
	 * @return true if listener was removed.
	 */
	public boolean removeWindowListener(WindowListener w) {
		return windowListenerList.remove(w);
	}
	/**
	 * Holds Listeners.
	 */
	private java.util.List windowListenerList = new LinkedList();
	/**
	 * Notify window listeners sending WindowEvent.
	 */
	private void close() {
		Iterator it = windowListenerList.iterator();
		while (it.hasNext()) {
			((WindowListener) it.next()).windowClosing(
				new WindowEvent(parent, WindowEvent.WINDOW_CLOSING));
		}
	}
	// --- end of window listener handling ---

	/**
	 * main for testing
	 * @param args
	 */
	public static void main(String[] args) {
		JFrame frame = new JFrame();
		ConfigDialog cd;
		try {
			cd = new ConfigDialog(frame,ConfigConstants.MODE_HTTP);
		} catch (BackingStoreException e1) {
			e1.printStackTrace();
			return;
		}
		cd.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.out.println(e);
				if (e.getID() == WindowEvent.WINDOW_CLOSING) {
					System.exit(0);
				}
			}
		});
		frame.getContentPane().add(cd);
		frame.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
		frame.pack();
		frame.show();
	}

	/**
	 * Class provides JTable based editor for Preferences.
	 * @author hans joachim herbertz
	 * created 24.02.2004
	 */
	private class PreferenceEditor extends JPanel implements ActionListener {

		/**
		 * Editable preference values representing text
		 */
		public static final int EDIT_TEXT = 1;
		/**
		 * Editable preference values represent files,
		 * e.g. an executable extension (like php.exe).
		 */
		public static final int EDIT_FILE = 2;
		/**
		 * Editable preference values represent directories
		 * e.g. alias directories (like /cgi-bin/)
		 */
		public static final int EDIT_DIRECTORIES = 3;
		private int mode = EDIT_TEXT;
		/**
		 * The display height of a table row.
		 */
		public static final int TABLE_ROW_HEIGHT = 25;
		/**
		 * The JTable that holds values
		 */
		private JTable table = null;

		/**
		 * Constructor
		 * @param prefs
		 * @param editType one of the above defined EDIT_TEXT, EDIT_FILE etc.
		 * @throws BackingStoreException
		 */
		public PreferenceEditor(Preferences prefs, int editType)
			throws BackingStoreException {
			this.mode = editType;
			this.setLayout(new BorderLayout());

			PreferenceTableModel model = new PreferenceTableModel(prefs);

			table = new JTable(model);
			table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
			table.getTableHeader().setReorderingAllowed(false);
			table.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					tableClicked();
				}
			});
			table.setRowHeight(TABLE_ROW_HEIGHT);
			this.add(new JScrollPane(table), BorderLayout.CENTER);

			JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
			JButton button = new JButton("Add");
			button.addActionListener(this);
			button.setActionCommand("add");
			p.add(button);
			button = new JButton("Delete");
			button.addActionListener(this);
			button.setActionCommand("delete");
			p.add(button);
			this.add(p, BorderLayout.SOUTH);
		}
		/**
		 * Set the table headlines
		 * @param headlines
		 */
		public void setTableHeadlines(String[] headlines) {
			getModel().setHeadlines(headlines);
		}
		/**
		 * Stores properties if changed
		 * @throws BackingStoreException
		 */
		public void store() throws BackingStoreException {
			getModel().store();
		}
		/**
		 * Gets the table model
		 * @return
		 */
		private PreferenceTableModel getModel() {
			return (PreferenceTableModel) table.getModel();
		}
		/**
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			if ("add".equals(e.getActionCommand())) {
				getModel().addRow();
				table.scrollRectToVisible(new Rectangle(0, 0));
			} else if ("delete".equals(e.getActionCommand())) {
				if (table.getSelectedRow() > -1) {
					getModel().deleteRow(table.getSelectedRow());
				} else {
					JOptionPane.showMessageDialog(this, "No row selected.");
				}
			}
		}

		/**
		 * Show JFileChooser depending on some editTypes.
		 */
		private void tableClicked() {
			int selectionMode = JFileChooser.FILES_ONLY;
			switch (mode) {
				case EDIT_DIRECTORIES :
					selectionMode = JFileChooser.DIRECTORIES_ONLY;

				case EDIT_FILE :
					int row = table.getSelectedRow();
					int col = table.getSelectedColumn();
					if (col == 0) {
						return;
					}
					File file = null;
					file = new File(getModel().getValueAt(row, col) + "");
					if (file.isFile())
						file = file.getParentFile();
					JFileChooser chooser = new JFileChooser(file);
					chooser.setFileSelectionMode(selectionMode);
					if (chooser.showOpenDialog(this)
						== JFileChooser.APPROVE_OPTION) {
						getModel().setValueAt(
							chooser.getSelectedFile().getAbsoluteFile(),
							row,
							col);
					}
					return;
				default :
					return;
			}
		}
	}

	/**
	 * Special editor for porxy server
	 */
	private class MailBasicsEditor extends ServerBasicsEditor {

		public MailBasicsEditor(Preferences prefs) {
			super(prefs);
			this.removeAll();
			setLayout(new BorderLayout());
			JPanel portPanel = new JPanel(new GridLayout(2, 1));
			portPanel.add(getPortPanel(ConfigConstants.MODE_MAIL+ConfigConstants.SETTING_PORT, 25));
			portPanel.add(new JPanel());
//			JPanel noCachingPanel = new JPanel(new GridLayout(2, 1));
//			noCachingPanel.add(portPanel);
//			noCachingPanel.add(getCachingPanel(ConfigConstants.PROXY_NO_CACHING_HEADERS));
			add(portPanel, BorderLayout.NORTH);
		}

		/**
		 * @see de.herberlin.server.ui.ConfigDialog.ServerBasicsEditor#store()
		 */
		public void store() {
			try {
				prefs.putInt(
					ConfigConstants.MODE_MAIL+ConfigConstants.SETTING_PORT,
					((Integer) spinner.getValue()).intValue());
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}

	}
	/**
	 * Editor for Docroot and port
	 * @author hans joachim herbertz
	 * created 24.02.2004
	 */
	private class ServerBasicsEditor extends JPanel implements ActionListener {

		protected Preferences prefs = null;
		private JTextField docrootTextField = null;
		protected JSpinner spinner = null;

		/**
		 * Constructor
		 * @param prefs
		 */
		public ServerBasicsEditor(Preferences prefs) {
			setLayout(new BorderLayout());
			JPanel p = new JPanel(new GridLayout(3, 1));
			JPanel p1 = new JPanel(new GridLayout(2, 1));
			this.prefs = prefs;
			p1.add(getDocrootPanel());
			p1.add(getPortPanel(ConfigConstants.MODE_HTTP+ConfigConstants.SETTING_PORT, 80));
			p.add(p1);
			p.add(getDefaultPagesPanel());
			p.add(getCachingPanel(ConfigConstants.HTTP_NO_CACHING_HEADERS));
			this.add(p, BorderLayout.NORTH);
		}
		/**
		 * Creates visible representation for the docroot
		 * @return
		 */
		private JPanel getDocrootPanel() {

			JPanel p = new JPanel(new BorderLayout());
			p.setBorder(BorderFactory.createTitledBorder("Document Root"));

			docrootTextField = new JTextField();
			docrootTextField.setText(
				prefs.get(
					ConfigConstants.HTTP_DOCROOT,
					System.getProperty("user.dir")));
			p.add(docrootTextField, BorderLayout.CENTER);

			JButton b = new JButton("Choose");
			b.setActionCommand("choose");
			b.addActionListener(this);
			p.add(b, BorderLayout.EAST);
			return p;
		}

		/**
		 * Stores changed properties
		 */
		public void store() {
			prefs.put(ConfigConstants.HTTP_DOCROOT, docrootTextField.getText());
			try {

				prefs.putInt(
						ConfigConstants.MODE_HTTP+ConfigConstants.SETTING_PORT,
					((Integer) spinner.getValue()).intValue());
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}

		protected JPanel getCachingPanel(final String key) {
			JPanel p = new JPanel(new GridLayout(1, 1));
			p.setBorder(BorderFactory.createTitledBorder("Caching"));

			JCheckBox avoidCachingButton = new JCheckBox("Avoid Caching");
			avoidCachingButton.setHorizontalTextPosition(JCheckBox.LEFT);
			avoidCachingButton.setHorizontalAlignment(JCheckBox.RIGHT);
			avoidCachingButton.setSelected(
				Configuration.getPrefs().getBoolean(key, true));
			avoidCachingButton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent a) {
					JCheckBox box = (JCheckBox) a.getSource();
					prefs.putBoolean(key, box.isSelected());
				}
			});
			p.add(avoidCachingButton);
			JPanel pp = new JPanel(new GridLayout(2, 1));
			pp.add(p);
			pp.add(new JPanel());
			return pp;
		}
		/**
		 * Creates the port editor
		 * @return
		 */
		protected JPanel getPortPanel(final String key, int defaultValue) {
			JPanel p = new JPanel(new BorderLayout());
			p.setBorder(BorderFactory.createTitledBorder("Port"));
			spinner =
				new JSpinner(
					new SpinnerNumberModel(
						Configuration.getPrefs().getInt(key, defaultValue),
						1,
						9999,
						1));
			p.add(spinner, BorderLayout.EAST);
			return p;
		}

		private JPanel getDefaultPagesPanel() {
			JPanel p = new JPanel(new GridLayout(2, 1));
			p.setBorder(BorderFactory.createTitledBorder("Default Pages"));

			JTextArea text =
				new JTextArea(
					prefs.get(
						ConfigConstants.HTTP_DEFAULT_PAGE_LIST,
						"index.html,index.htm,index.shtml"));
			p.add(new JScrollPane(text));
			text.addFocusListener(new FocusAdapter() {
				public void focusLost(FocusEvent e) {
					JTextComponent c = (JTextComponent) e.getSource();
					String[] elements = c.getText().split("[\\s,;]+");
					StringBuffer buffer = new StringBuffer();
					for (int i = 0; i < elements.length; i++) {
						buffer.append(elements[i]);
						if (i < elements.length - 1) {
							buffer.append(", ");
						}
					}
					String t = buffer.toString();
					c.setText(t);
					prefs.put(ConfigConstants.HTTP_DEFAULT_PAGE_LIST, t);
				}
			});

			JCheckBox checkBox = new JCheckBox("Use Default Pages");
			checkBox.setSelected(
				prefs.getBoolean(ConfigConstants.HTTP_USE_DEFAULT_PAGES, true));
			checkBox.setHorizontalTextPosition(JCheckBox.LEFT);
			checkBox.setHorizontalAlignment(JCheckBox.RIGHT);
			checkBox.addItemListener(new ItemListener() {
				public void itemStateChanged(ItemEvent e) {
					if (e.getStateChange() == ItemEvent.DESELECTED) {
						prefs.putBoolean(
							ConfigConstants.HTTP_USE_DEFAULT_PAGES,
							false);
					} else {
						prefs.putBoolean(
							ConfigConstants.HTTP_USE_DEFAULT_PAGES,
							true);
					}
				}
			});
			p.add(checkBox);

			return p;
		}
		/**
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			if ("choose".equals(e.getActionCommand())) {
				JFileChooser chooser =
					new JFileChooser(docrootTextField.getText());
				chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
				if (chooser.showOpenDialog(this)
					== JFileChooser.APPROVE_OPTION) {
					docrootTextField.setText(
						chooser.getSelectedFile().getAbsolutePath());
				}
			}
		}
	}
	/**
	 * Special editor for porxy server
	 */
	private class ProxyBasicsEditor extends ServerBasicsEditor {

		public ProxyBasicsEditor(Preferences prefs) {
			super(prefs);
			this.removeAll();
			setLayout(new BorderLayout());
			JPanel p = new JPanel(new GridLayout(2, 1));
			JPanel p2 = new JPanel(new GridLayout(2, 1));
			p2.add(getPortPanel(ConfigConstants.MODE_PROXY+ConfigConstants.SETTING_PORT, 2505));
			p2.add(new JPanel());
			p.add(p2);
			p.add(getCachingPanel(ConfigConstants.PROXY_NO_CACHING_HEADERS));
			add(p, BorderLayout.NORTH);
		}

		/**
		 * @see de.herberlin.server.ui.ConfigDialog.ServerBasicsEditor#store()
		 */
		public void store() {
			try {
				prefs.putInt(
					ConfigConstants.MODE_PROXY+ConfigConstants.SETTING_PORT,
					((Integer) spinner.getValue()).intValue());
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}

	}
	/**
	 * @see java.awt.Component#getPreferredSize()
	 */
	public Dimension getPreferredSize() {
		return new Dimension(320, 360);
	}

}
